home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Modules / BackSpaceModules / Source / FlippyFloppy / FlippyFloppyView.m < prev    next >
Text File  |  1992-12-07  |  16KB  |  572 lines

  1. /*-
  2.  
  3.  FlippyFloppyView.m
  4.  
  5.  Warp, twist, and bounce a piece of text around the screen.
  6.  
  7.  Copyright (c) 1992 by Scott Byer
  8.  
  9.  Permission is given to freely distribute and modify this code
  10.  provided the copyright notice is left unchanged.
  11.  No warranties, explicit or implied, apply to this code.
  12. */
  13.  
  14. #import "FlippyFloppyView.h"
  15.  
  16. #import <appkit/appkit.h>
  17.  
  18. #import <dpsclient/dpsclient.h>
  19. #import <dpsclient/psops.h>
  20.  
  21. #import <libc.h>
  22. #import <stdlib.h>
  23.  
  24. #include "FlippyFloppyWraps.h"
  25.  
  26. #define    XMOVEFACTOR    (0.1 * hyperness)
  27. #define YMOVEFACTOR    (0.1 * hyperness)
  28.  
  29. #define XSPEEDMAX    (20.0 * hyperness)
  30. #define YSPEEDMAX    (20.0 * hyperness)
  31. #define XSPEEDMIN    (-20.0 * hyperness)
  32. #define YSPEEDMIN    (-20.0 * hyperness)
  33.  
  34. #define ROTATEFACTOR    (0.5 * hyperness)
  35.  
  36. #define ROTATESPEEDMAX    (20.0 * hyperness)
  37. #define ROTATESPEEDMIN    (-20.0 * hyperness)
  38.  
  39. #define XSCALEFACTOR    (0.05 * hyperness)
  40. #define YSCALEFACTOR    (0.05 * hyperness)
  41.  
  42. #define XSCALESPEEDMAX    (0.1 * hyperness)
  43. #define YSCALESPEEDMAX    (0.1 * hyperness)
  44. #define XSCALESPEEDMIN    (-0.1 * hyperness)
  45. #define YSCALESPEEDMIN    (-0.1 * hyperness)
  46.  
  47. #define XSCALEMIN    -5.0
  48. #define YSCALEMIN    -5.0
  49. #define XSCALEMAX    5.0
  50. #define YSCALEMAX    5.0
  51.  
  52. #define SCALENOMANSLAND    0.08
  53.  
  54. #define COLORFACTOR    (0.01 * hyperness)
  55.  
  56. #define COLORSPEEDMAX    (0.05 * hyperness)
  57. #define COLORSPEEDMIN    (-0.05 * hyperness)
  58.  
  59. #define COLORMIN    0.1
  60. #define COLORMAX    1.0
  61.  
  62. @implementation FlippyFloppyView
  63.  
  64. #define    DRANDOM(factor)    ((random() / 2147483647.0) * (factor) - ((factor) / 2.0))
  65.  
  66. - oneStep
  67.   {
  68.     int    whichLine;
  69.  
  70.     /* Erase the old text by doing a transform and drawing a black
  71.        rectangle.                               */
  72.     FFWTransform(x, y, xscale, yscale, rotation);
  73.     FFWBlackBox(stringsBBox.llx, stringsBBox.lly,
  74.                   stringsBBox.urx, stringsBBox.ury);
  75.     FFWDeTransform(-x, -y, 1.0/xscale, 1.0/yscale, -rotation);
  76.  
  77.     /* If the font or the text to display has changed, go figure out what
  78.        is now different, and act accordingly. */
  79.     if (changed) {
  80.       [self somethingChanged];
  81.       changed = NO;
  82.       }
  83.       
  84.     /* Sanity check to make sure we have at least one string to display. */
  85.     if (numLines == 0) return self;
  86.      
  87.     /* Change the movemoent of the characters                   */
  88.     xspeed += DRANDOM(XMOVEFACTOR);
  89.     yspeed += DRANDOM(YMOVEFACTOR);
  90.  
  91.     /* Bounds checking for the movement speed.                   */
  92.     if (xspeed < XSPEEDMIN) xspeed = XSPEEDMIN;
  93.     if (xspeed > XSPEEDMAX) xspeed = XSPEEDMAX;
  94.  
  95.     if (yspeed < YSPEEDMIN) yspeed = YSPEEDMIN;
  96.     if (yspeed > YSPEEDMAX) yspeed = YSPEEDMAX;
  97.  
  98.     /* Change to location of the characters based on the new movement.       */
  99.     x += xspeed;
  100.     y += yspeed;
  101.  
  102.     /* Bounds checking for the absolute location.               */
  103.     if (x < 0.0) {
  104.     x = 0.0;
  105.     xspeed = -xspeed;
  106.       }
  107.     if (x > xmax) {
  108.     x = xmax;
  109.     xspeed = -xspeed;
  110.       }
  111.  
  112.     if (y < 0.0) {
  113.     y = 0.0;
  114.     yspeed = -yspeed;
  115.       }
  116.     if (y > ymax) {
  117.     y = ymax;
  118.     yspeed = -yspeed;
  119.       }
  120.  
  121.     /* Change the rate of rotation.                       */
  122.     rotationspeed += DRANDOM(ROTATEFACTOR);
  123.  
  124.     /* Bounds check the rotation rate.                       */
  125.     if (rotationspeed < ROTATESPEEDMIN) rotationspeed = ROTATESPEEDMIN;
  126.     if (rotationspeed > ROTATESPEEDMAX) rotationspeed = ROTATESPEEDMAX;
  127.  
  128.     /* Add the rotation in.                           */
  129.     rotation += rotationspeed;
  130.  
  131.     /* Bounds check the rotation angle.                       */
  132.     if (rotation < 0.0) rotation += 360.0;
  133.     if (rotation > 360.0) rotation -= 360.0;
  134.  
  135.     /* Change the rate of scaling.                       */
  136.     xscalespeed += DRANDOM(XSCALEFACTOR);
  137.     yscalespeed += DRANDOM(YSCALEFACTOR);
  138.  
  139.     /* Check the scale rate agains the boundaries.               */
  140.     if (xscalespeed < XSCALESPEEDMIN) xscalespeed = XSCALESPEEDMIN;
  141.     if (xscalespeed > XSCALESPEEDMAX) xscalespeed = XSCALESPEEDMAX;
  142.  
  143.     if (yscalespeed < YSCALESPEEDMIN) yscalespeed = YSCALESPEEDMIN;
  144.     if (yscalespeed > YSCALESPEEDMAX) yscalespeed = YSCALESPEEDMAX;
  145.  
  146.     /* Check the scaling factor.                       */
  147.     xscale += xscalespeed;
  148.     yscale += yscalespeed;
  149.  
  150.     /* Work around what seemed like a rasteriation bug in 3.0. */
  151.     while ((xscale > -SCALENOMANSLAND) && (xscale < SCALENOMANSLAND))
  152.         xscale += xscalespeed;
  153.     while ((yscale > -SCALENOMANSLAND) && (yscale < SCALENOMANSLAND))
  154.         yscale += yscalespeed;
  155.  
  156.     /* Bounds check the scaling.                       */
  157.     if (xscale < XSCALEMIN) {
  158.     xscale = XSCALEMIN;
  159.     xscalespeed = -xscalespeed;
  160.       }
  161.     if (xscale > XSCALEMAX) {
  162.     xscale = XSCALEMAX;
  163.     xscalespeed = -xscalespeed;
  164.       }
  165.  
  166.     if (yscale < YSCALEMIN) {
  167.     yscale = YSCALEMIN;
  168.     yscalespeed = -yscalespeed;
  169.       }
  170.     if (yscale > YSCALEMAX) {
  171.     yscale = YSCALEMAX;
  172.     yscalespeed = -yscalespeed;
  173.       }
  174.  
  175.     /* Calculate the new color bounce speeds.                   */
  176.     rspeed += DRANDOM(COLORFACTOR);
  177.     gspeed += DRANDOM(COLORFACTOR);
  178.     bspeed += DRANDOM(COLORFACTOR);
  179.  
  180.     /* Bounds check the color speeds.                       */
  181.     if (rspeed < COLORSPEEDMIN) rspeed = COLORSPEEDMIN;
  182.     if (rspeed > COLORSPEEDMAX) rspeed = COLORSPEEDMAX;
  183.  
  184.     if (gspeed < COLORSPEEDMIN) gspeed = COLORSPEEDMIN;
  185.     if (gspeed > COLORSPEEDMAX) gspeed = COLORSPEEDMAX;
  186.  
  187.     if (bspeed < COLORSPEEDMIN) bspeed = COLORSPEEDMIN;
  188.     if (bspeed > COLORSPEEDMAX) bspeed = COLORSPEEDMAX;
  189.  
  190.     /* Change the colors by the speeds.                       */
  191.     r += rspeed;
  192.     g += gspeed;
  193.     b += bspeed;
  194.  
  195.     /* Bounds check the color.                           */
  196.     if (r < COLORMIN) {
  197.     r = COLORMIN;
  198.     rspeed = -rspeed;
  199.       }
  200.     if (r > COLORMAX) {
  201.     r = COLORMAX;
  202.     rspeed = -rspeed;
  203.       }
  204.  
  205.     if (g < COLORMIN) {
  206.     g = COLORMIN;
  207.     gspeed = -gspeed;
  208.       }
  209.     if (g > COLORMAX) {
  210.     g = COLORMAX;
  211.     gspeed = -gspeed;
  212.       }
  213.  
  214.     if (b < COLORMIN) {
  215.     b = COLORMIN;
  216.     bspeed = -bspeed;
  217.       }
  218.     if (b > COLORMAX) {
  219.     b = COLORMAX;
  220.     bspeed = -bspeed;
  221.       }
  222.       
  223.     /* Transform the coordinate system and draw the text.           */
  224.     FFWTransform(x, y, xscale, yscale, rotation);
  225.     PSsetrgbcolor(r, g, b);
  226.     for ( whichLine = 0 ; whichLine < numLines ; whichLine++ ) {
  227.       FFWText(startStrings[whichLine].x, startStrings[whichLine].y,
  228.               displayStrings[whichLine]);
  229.       }
  230.     FFWDeTransform(-x, -y, 1.0/xscale, 1.0/yscale, -rotation);
  231.  
  232.     return self;
  233.   }
  234.  
  235. - didLockFocus
  236.   {
  237.     [textFont set];
  238.     return self;
  239.   }
  240.  
  241. - initFrame:(NXRect *)frameRect
  242.   {
  243.     [super initFrame:frameRect];
  244.     
  245.     [inspectorPanel display];
  246.     [self newSize];
  247.     [self initValues];
  248.      
  249.     srandom(time(0));
  250.     return self;
  251.   }
  252.  
  253. -initValues
  254.   {
  255.     /* Initialize the position.                           */
  256.     x = xmax / 2.0;
  257.     y = ymax / 2.0;
  258.     r = g = b = 0.5;
  259.     rotation = 5.0;
  260.     xscale = yscale = 1.0;
  261.  
  262.     /* Initialize the rate of change                       */
  263.     xspeed = yspeed = 5.0;
  264.     rotationspeed = 10.0;
  265.  
  266.     /* Initialize the overall control factor.                   */
  267.     hyperness = 1.0;
  268.       
  269.     return self;
  270.   }
  271.  
  272. - inspector:sender
  273. {
  274.     static NXDefaultsVector FlippyFloppyDefaults = {
  275.         { "NXFont", "Times-Roman" },
  276.     { "Phrase", "Flippy\nFloppy" },
  277.     { NULL, NULL }};
  278.  
  279.     char buf[MAXPATHLEN];
  280.  
  281.     if (!inspectorPanel) {
  282.       sprintf(buf,"%s/FlippyFloppy.nib",[sender
  283.           moduleDirectory:"FlippyFloppy"]);
  284.       [NXApp loadNibFile:buf owner:self withNames:NO];
  285.  
  286.       /* Register our defaults. */
  287.       NXRegisterDefaults("BackSpace.FlippyFloppy", FlippyFloppyDefaults);
  288.  
  289.       changed = YES;
  290.       }
  291.       
  292.     return inspectorPanel;
  293. }
  294.  
  295.   /* Attach ourselves to the end of a window's responder chain. */
  296. - add:someone ToResponderListOf: whichWindow
  297.   {
  298.     id        prevResp, curResp;
  299.     
  300.     /* Put ourselves at the end of a window's responder chain. */
  301.     prevResp = curResp = whichWindow;
  302.     while ((curResp != someone) && (curResp != nil)) {
  303.         /* Guarunteed to go through once. */
  304.         prevResp = curResp;
  305.     curResp = [curResp nextResponder];
  306.     }
  307.     if (curResp == nil) [prevResp setNextResponder:someone];
  308.     return self;
  309.   }
  310.   
  311.   /* Remove ourselves from anywhere within a window's responder chain. */
  312. - remove:someone FromResponderListOf: whichWindow
  313.   {
  314.     id        prevResp, curResp;
  315.  
  316.     /* Remove ourselve from the middle or end of a responder chain. */
  317.     prevResp = curResp = whichWindow;
  318.     while ((curResp != someone) && (curResp != nil)) {
  319.         /* Guarunteed to go through once. */
  320.         prevResp = curResp;
  321.     curResp = [curResp nextResponder];
  322.     }
  323.     if (curResp == someone) [prevResp setNextResponder:
  324.                              [someone nextResponder]];
  325.     return self;
  326.   }
  327.   
  328.  
  329. - inspectorInstalled
  330.   {
  331.     const char    *fontName, *phrase;
  332.  
  333.     /* Get ourselves access to the font panel. */    
  334.     fontManager = [FontManager new];
  335.     fontPanel = [fontManager getFontPanel:YES];
  336.     
  337.     /* We better not have a nextResponder before placing ourselves in
  338.        the middle of these responder lists. */
  339.     [self setNextResponder:nil];
  340.     
  341.     /* Get ourselves placed at the end of the responder chains for the
  342.        windows we are dealing with. */
  343.     [self add:self ToResponderListOf:fontPanel];
  344.     
  345.     /* Make the font panel a responder of the other two windows. */
  346.     [self add:fontPanel ToResponderListOf:[inspectorPanel window]];
  347.     [self add:fontPanel ToResponderListOf:[self window]];
  348.     
  349.     /* Make the FontManager use this module's action message. */
  350.     [fontManager setAction:@selector(changeFontFlippyFloppy:)];
  351.  
  352.     /* Get the default font from the defaults database. */
  353.     fontName = NXGetDefaultValue("BackSpace.FlippyFloppy", "NXFont");
  354.     textFont = [Font newFont:fontName size:24 matrix:NX_IDENTITYMATRIX];
  355.     [fontPanel setPanelFont:textFont isMultiple:NO];
  356.     [fontNameDisplay setStringValue:[textFont displayName]];
  357.     [flippyFloppyButton display];
  358.  
  359.     /* Get the default phrase from the database. */
  360.     phrase = NXGetDefaultValue("BackSpace.FlippyFloppy", "Phrase");
  361.     [textPanel setStringValue:phrase];
  362.  
  363.     changed = YES;
  364.     
  365.     return self;
  366.   }
  367.   
  368. - inspectorWillBeRemoved
  369.   {
  370.     /* Hide the font panel. */
  371.     [fontPanel orderWindow:NX_OUT relativeTo:0];     
  372.  
  373.     /* Make the FontManager use the generic action message. */
  374.     [fontManager setAction:@selector(changeFont:)];
  375.  
  376.     /* Remove the fontpanel from the other window's responder lists.
  377.        Remove ourselves from the fontPanel's responder list.  */
  378.     [self remove:self FromResponderListOf:fontPanel];
  379.     [self remove:fontPanel FromResponderListOf:[inspectorPanel window]];
  380.     [self remove:fontPanel FromResponderListOf:[self window]];
  381.         
  382.     /* Restore the cache limit parameter for the context. */
  383.     if (cacheLimit == 0) cacheLimit = 12500;
  384.     FFWsetcachelimit(cacheLimit);
  385.  
  386.     return self;
  387.   }
  388.  
  389. - sizeTo:(NXCoord)width :(NXCoord)height
  390.   {
  391.     [super sizeTo:width :height];
  392.     [self newSize];
  393.     return self;
  394.   }
  395.  
  396. - newSize
  397.   {
  398.     xmax = bounds.size.width;
  399.     ymax = bounds.size.height;
  400.     return self;
  401.   }
  402.  
  403. - drawSelf:(const NXRect *)rects :(int)rectCount
  404.   {
  405.     if (!rects || !rectCount)
  406.       {
  407.     return self;
  408.       }
  409.     PSsetgray(0.0);
  410.     NXRectFill(rects);
  411.     return self;
  412.   }
  413.  
  414. - (BOOL)useBufferedWindow
  415.   {
  416.     return YES;
  417.   }
  418.  
  419. - (const char *)windowTitle
  420.   {
  421.     return "Make FlippyFloppy!";
  422.   }
  423.  
  424. - setHyperness: sender
  425.   {
  426.     hyperness = [sender floatValue];
  427.     [hypernessField setFloatValue:hyperness];
  428.     return self;
  429.   }
  430.  
  431. - setFlippyText: sender
  432.   {
  433.    [textPanel selectText:self];
  434.    NXWriteDefault("BackSpace.FlippyFloppy", "Phrase", [textPanel stringValue]);
  435.    changed = YES;
  436.    return self;
  437.   }
  438.  
  439. - changeFontFlippyFloppy: sender
  440.   {
  441.    textFont = [sender convertFont:textFont];
  442.    NXWriteDefault("BackSpace.FlippyFloppy", "NXFont", [textFont name]);
  443.    [fontNameDisplay setStringValue:[textFont displayName]];
  444.    [flippyFloppyButton display];
  445.    changed = YES;
  446.    return sender;
  447.   }
  448.  
  449. - setFloppyFont: sender
  450.   { 
  451.     /* Make the FontManager use this module's action message. */
  452.     [fontManager setAction:@selector(changeFontFlippyFloppy:)];
  453.     
  454.     /* Make the font panel appear! */
  455.     [[fontPanel setPanelFont:textFont isMultiple:NO]
  456.       makeKeyAndOrderFront:[inspectorPanel window]];     
  457.  
  458.     return self;
  459.   }
  460.   
  461. - somethingChanged
  462.   {
  463.     const char    *textToSplit, *splitStart;
  464.     int        whichLine, oldCacheLimit;
  465.     TextBBox    linesBBox[MAX_TEXT_LINES];
  466.     double    tweak, baseline;
  467.  
  468.     /* Zero out the cache limit - this is a *per context* variable (which is
  469.        why we do it here - to make *sure* the context we are flipping text in
  470.        has it temporarily zeroed out. */
  471.     FFWzerocachelimit(&oldCacheLimit);
  472.     if (oldCacheLimit != 0) cacheLimit = oldCacheLimit;
  473.  
  474.     /* Make sure we are using the right font. */
  475.     [textFont set];
  476.     [fontNameDisplay setStringValue:[textFont displayName]];
  477.     [flippyFloppyButton display];
  478.     
  479.     /* Free the previous chunks of text, if any. */
  480.     for ( whichLine = 0 ; whichLine < numLines ; whichLine++ ) {
  481.       if (displayStrings[whichLine] != NULL) {
  482.         (void)free(displayStrings[whichLine]);
  483.     displayStrings[whichLine] = NULL;
  484.     }
  485.       }
  486.       
  487.     /* Figure out how many lines there are to display, keeping track of where
  488.        each line begins.                           */
  489.     whichLine = 0;
  490.     textToSplit = [textPanel stringValue];
  491.     if (textToSplit == NULL) return NULL;
  492.     
  493.     do {
  494.       splitStart = textToSplit;
  495.       while ((*textToSplit != 0) && (*textToSplit != '\n'))
  496.         textToSplit++;
  497.  
  498.       /* Allocate the string chunk and copy the right part of the string. */
  499.       displayStrings[whichLine] = malloc(textToSplit - splitStart + 1);
  500.       if (displayStrings[whichLine] != NULL) {
  501.         strncpy(displayStrings[whichLine], splitStart, 
  502.             textToSplit - splitStart);
  503.         displayStrings[whichLine][textToSplit-splitStart] = 0;
  504.     whichLine++;
  505.     }
  506.     
  507.       /* Turn returns into string endings.                   */
  508.       if (*textToSplit == '\n') textToSplit++;
  509.  
  510.     } while ((*textToSplit != 0) && (whichLine < MAX_TEXT_LINES));
  511.  
  512.     numLines = whichLine;
  513.  
  514.     /* Figure out the bounding boxes for each string, and at the same time
  515.        what the total bounding box will be.                   */
  516.     stringsBBox.llx = 100.0;
  517.     stringsBBox.urx = -100.0;
  518.     stringsBBox.lly = 0.0;
  519.     stringsBBox.ury = 0.0;
  520.  
  521.     for (whichLine = 0 ; whichLine < numLines ; whichLine++) {
  522.       FFWTextBBox(displayStrings[whichLine],
  523.           &linesBBox[whichLine].llx, &linesBBox[whichLine].lly,
  524.           &linesBBox[whichLine].urx, &linesBBox[whichLine].ury);
  525.  
  526.       /* The strings are centered over one another in the x direction.       */
  527.       if (linesBBox[whichLine].llx < stringsBBox.llx)
  528.     stringsBBox.llx = linesBBox[whichLine].llx;
  529.       if (linesBBox[whichLine].urx > stringsBBox.urx)
  530.     stringsBBox.urx = linesBBox[whichLine].urx;
  531.  
  532.       /* The strings sit on top of one another in the y direction.       */
  533.       stringsBBox.lly += linesBBox[whichLine].lly;
  534.       stringsBBox.ury += linesBBox[whichLine].ury + 2.0;
  535.       }
  536.  
  537.     /* Now, tweak the bounding box to reflect the desire to have it centered
  538.        around the origin.                           */
  539.     tweak = (stringsBBox.llx + stringsBBox.urx) / 2.0;
  540.     stringsBBox.llx -= tweak;
  541.     stringsBBox.urx -= tweak;
  542.  
  543.     tweak = (stringsBBox.lly + stringsBBox.ury) / 2.0;
  544.     stringsBBox.lly -= tweak;
  545.     stringsBBox.ury -= tweak;
  546.  
  547.     /* Now, for each line of text, figure out where it's starting point is
  548.        going to be.                               */
  549.     baseline = stringsBBox.ury;
  550.     for ( whichLine = 0 ; whichLine < numLines ; whichLine++ ) {
  551.  
  552.       tweak = (linesBBox[whichLine].llx + linesBBox[whichLine].urx) / 2.0;
  553.       startStrings[whichLine].x = -tweak;
  554.  
  555.       baseline -= (linesBBox[whichLine].ury + 2.0);
  556.       startStrings[whichLine].y = baseline;
  557.       }
  558.  
  559.     /* Bump out the bounding box slightly. */
  560.     stringsBBox.llx -= 6.0;    stringsBBox.lly -= 6.0;
  561.     stringsBBox.urx += 6.0;    stringsBBox.ury += 6.0;
  562.  
  563.     /* Convert the bounding box into a rectangle.               */
  564.     stringsBBox.urx -= stringsBBox.llx;
  565.     stringsBBox.ury -= stringsBBox.lly;
  566.  
  567.     return self;
  568.   }
  569.   
  570. @end
  571.  
  572.